package com.fstec.ks.master.netty;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.apache.commons.beanutils.converters.SqlTimestampConverter;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.lang.math.RandomUtils;
import org.hyperic.sigar.NetInterfaceConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.fstec.ks.master.MasterEngine;
import com.fstec.ks.master.bean.ExamRoundBean;
import com.fstec.ks.master.bean.ExamRunInfo;
import com.fstec.ks.master.bean.ExamServerBean;
import com.fstec.ks.master.bean.RoomExamInfo;
import com.fstec.ks.master.bean.RoomRoundHolder;
import com.fstec.ks.master.bean.RoundExamInfo;
import com.fstec.ks.master.bean.ServerInfoBean;
import com.fstec.ks.master.bean.ServerRoundHolder;
import com.fstec.ks.master.service.AuthService;
import com.fstec.ks.master.service.ExamDataService;
import com.fstec.ks.master.vo.ExamRoundVO;
import com.fstec.ks.master.vo.ExamServerVO;
import com.fstec.ks.master.vo.RoomRoundVO;
import com.fstec.ks.master.vo.ServerInfoVO;
import com.fstec.ks.master.vo.ServerRoundVO;
import com.sinotn.SinotnLogger;
import com.sinotn.SinotnRuntime;
import com.sinotn.Version;
import com.sinotn.beanutils.UtilDateConverter;
import com.sinotn.error.MessageException;
import com.sinotn.id.SeqUUIDGenerator;
import com.sinotn.ks.definition.Computer;
import com.sinotn.ks.definition.MessageReceiver;
import com.sinotn.ks.definition.RequestMessage;
import com.sinotn.ks.definition.SocketServer;
import com.sinotn.secret.CommonSecretor;
import com.sinotn.sigar.SigarUtils;
import com.sinotn.task.TaskServer;
import com.sinotn.util.FileUtil;

/**
 * 监控引擎实现
 * @Title DefaultMasterEngine.java
 * @Package com.fstec.ks.master.vendor
 * @Description 
 * Copyright: Copyright (c) 2023
 * Company:广东峰上科技有限公司
 * 
 * @author <a href="mailto:xieqj@fstec.com">谢启进</a>
 * @date 2023年10月10日 上午10:34:56
 * @version V1.0
 */
public class DefaultMasterEngine implements MasterEngine,InitializingBean,Runnable,ApplicationContextAware {
	@Autowired
	private AuthService authService;
	@Autowired
	private ExamDataService examDataService;
	@Autowired
	private TaskServer taskServer;
	private String version="0.0.0";
	private String clientVersion="0.0.0";
	private ReentrantLock serverRegLock=new ReentrantLock();
	private boolean debug=SinotnLogger.DEBUG.isDebugEnabled();
	//Socket通讯服务
	private SocketServer socketServer;
	//Socket通讯服务启动延迟时间（单位：毫秒）
	private long socketServerLazy=5000;
	private boolean socketStarted=false;
	private ServerInfoBean serverInfo;
	private List<ExamRoundBean> examRounds;
	// 考试服务器信息(key:注册Id)
	private ConcurrentHashMap<String, ExamServerVO> examServers=new ConcurrentHashMap<String, ExamServerVO>();
	// 基于场次时间考试服务器分组管理(key:beginTimestamp_endTimestamp)
	private ConcurrentHashMap<String, ServerRoundHolder> serverRounds=new ConcurrentHashMap<String, ServerRoundHolder>();
	// 基于场次时间考场分组管理(key:beginTimestamp_endTimestamp)
	private ConcurrentHashMap<String, RoomRoundHolder> roomRounds=new ConcurrentHashMap<String, RoomRoundHolder>();
	/*
	 * 消息处理器集合
	 */
	private ConcurrentHashMap<Integer, MessageReceiver> receivers=new ConcurrentHashMap<Integer, MessageReceiver>();
	/*
	 * 终端已建立与监控服务器连接，但未授权验证。
	 * key为socket唯一标识。
	 */
	private ConcurrentHashMap<String, Computer> unknowComputers=new ConcurrentHashMap<String, Computer>();
	/*
	 * 已授权连接考试服务器
	 * key为注册Id(km_exam_server#id)
	 */
	private ConcurrentHashMap<String, Computer> serverComputers=new ConcurrentHashMap<String, Computer>();
	private long dataTime;
	private long queryTime;
	private String currentRound="";
	
	@Override
	public String getVersion() {
		return this.version;
	}

	@Override
	public String getClientVersion() {
		return this.clientVersion;
	}
	
	public void setSocketServer(SocketServer socketServer) {
		this.socketServer = socketServer;
	}

	public void close(){
		if(this.socketServer!=null){
			this.socketServer.close();
		}
	}
	
	private void touchData(){
		this.dataTime=System.currentTimeMillis();
	}

	@Override
	public ServerInfoBean getServerInfo() {
		return this.serverInfo;
	}

	@Override
	public String refreshAuth() {
		String newAuth=this.nextAuth();
		this.serverInfo.setAuthNum(newAuth);
		this.authService.modifyAuth(this.serverInfo.getId(), newAuth);
		return newAuth;
	}

	@Override
	public ServerInfoBean rebindIpAddr(String ipAddr) {
		this.serverInfo.setIpAddr(ipAddr);
		this.authService.modifyIpAddr(this.serverInfo.getId(), ipAddr);
		return this.serverInfo;
	}

	@Override
	public void onComputerConnect(Computer computer) {
		int vendor=computer.getVendor();
		if(vendor==Computer.VENDOR_MANAGE){
			String regKey=computer.getRegKey();
			if(regKey==null){
				SinotnLogger.DEBUG.error("{}连接没有注册key", computer.getSocketId());
				throw new MessageException(ResponseMessageType.BIZ_REG_INFO, "注册key已过期"+regKey);
			}else{
				ExamServerVO examServer=this.examServers.get(regKey);
				if(examServer==null){
					SinotnLogger.DEBUG.error("{}连接未识别的注册key={}", computer.getSocketId(), regKey);
					throw new MessageException(ResponseMessageType.BIZ_REG_INFO, "注册key已过期"+regKey);
				}else{
					this.serverComputers.put(regKey, computer);
					this.unknowComputers.remove(computer.getSocketId());
					examServer.setServerStatus(ExamServerVO.ServerStatus.CONNECT.getVal());
					examServer.setLastConnTime(new Date());
					// this.examDataService.saveExamServer(examServer); 不保存连接状态
					this.touchData();
				}
			}
		}else{
			this.unknowComputers.put(computer.getSocketId(), computer);
		}
	}

	@Override
	public void onComputerDisconnect(Computer computer) {
		String regKey=computer.getRegKey();
		String socketId=computer.getSocketId();
		if(regKey!=null){
			Computer old=this.serverComputers.get(regKey);
			if(old!=null&&old.getSocketId().equals(socketId)){
				this.serverComputers.remove(regKey);
				ExamServerVO examServer=this.examServers.get(regKey);
				if(examServer!=null && ExamServerVO.ServerStatus.CONNECT.is(examServer.getServerStatus())){
					examServer.setServerStatus(ExamServerVO.ServerStatus.DISCONN.getVal());
					// this.examDataService.saveExamServer(examServer); 不保存连接状态
					this.touchData();
				}
			}
		}

		this.unknowComputers.remove(socketId);
	}

	@Override
	public void onSocketException(Computer computer, Throwable cause) {
		if(computer==null){
			SinotnLogger.DEBUG.error("客户端程序Socket通讯异常 unknow-computer",cause);
		}else{
			if(cause instanceof IOException){
				String msg=cause.getMessage();
				if(msg.indexOf("强迫关闭")==-1&&msg.indexOf("中止")==-1){
					SinotnLogger.DEBUG.error("客户端程序发生IOException:"+computer.toString(), cause);
				}/*else{
					SinotnLogger.DEBUG.error("客户端程序强迫关闭:"+computer.toString());
				}*/
			}else if(cause instanceof DecoderException){
				SinotnLogger.DEBUG.error("数据解码发生系统错误"+computer.toString(),cause);
				//出现解码异常则关闭当前channel
				computer.disconnect();
			} else{
				SinotnLogger.DEBUG.error("客户端程序Socket通讯异常"+computer.toString(),cause);
			}
		}
	}

	@Override
	public void onMessageReceive(Computer computer, RequestMessage msg) {
		if(computer==null){
			throw new ExamExceptionMessage(ResponseMessageType.ERR_BIZ,"客户端计算机参数不能为NULL");
		}
		if(msg==null){
			throw new ExamExceptionMessage(ResponseMessageType.ERR_BIZ,"接收消息参数不能为NULL");
		}
		if(this.debug){
			SinotnLogger.DEBUG.debug("客户端消息："+msg.toString()+"@"+computer.getIpAddr());
		}
		MessageReceiver receiver=this.receivers.get(msg.getType());
		if(receiver==null){
			SinotnLogger.DEBUG.error("未能识别的消息"+msg.toString()+"@"+computer.toString());
			ExamExceptionMessage e=new ExamExceptionMessage(ResponseMessageType.ERR_BIZ, "未能识别的消息类别:"+msg.getType());
			e.setDisconnect(true);
			throw e;
		}else{
			receiver.process(computer,msg);
		}
	}

	@Override
	public void run() {
		try{
			this.serverInfo=new ServerInfoBean();
			this.serverInfo.setPort(this.socketServer.getPort());
			//预加载数据
			ServerInfoVO serverInfoVo=this.authService.getServerInfo();
			if(serverInfoVo==null){
				serverInfoVo=new ServerInfoVO();
				serverInfoVo.setAuthTime(new Date());
				serverInfoVo.setAuthNum(this.nextAuth());
				serverInfoVo.setId(SeqUUIDGenerator.genSequenceUUID());
				serverInfoVo.setIpAddr(this.getDefaultIpAddr());
				this.authService.addServerInfo(serverInfoVo);
			}
			this.serverInfo.setAuthNum(serverInfoVo.getAuthNum());
			this.serverInfo.setId(serverInfoVo.getId());
			this.serverInfo.setIpAddr(serverInfoVo.getIpAddr());
			// 加载业务数据到内存
			this.loadData();
			
			//启动socket服务
			if(!this.socketStarted){
				if(this.socketServerLazy>0){
					Thread.sleep(this.socketServerLazy);
				}
				this.socketServer.start();
				this.socketStarted=true;
			}
			
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("监测引擎数据加载逻辑发生系统错误",e);
			System.exit(1);
		}
	}

	private void loadData() {
		// 加载基础场次时间数据
		List<ExamRoundVO> rounds=this.examDataService.getAllExamRounds();
		if(!rounds.isEmpty()){
			ExamRoundVO roundVo;
			ExamRoundBean roundBean;
			int size=rounds.size();
			ArrayList<ExamRoundBean> beans=new ArrayList<ExamRoundBean>(size);
			for(int i=0;i<size;i++){
				roundVo=rounds.get(i);
				roundBean=new ExamRoundBean();
				beans.add(roundBean);
				roundBean.setBeginTime(roundVo.getBeginTime());
				roundBean.setEndTime(roundVo.getEndTime());
			}
			this.examRounds=beans;
		}
		// 加载注册服务器信息
		List<ExamServerVO> servers=this.examDataService.getAllExamServers();
		if(!servers.isEmpty()){
			ExamServerVO serverVo;
			int size=servers.size();
			for(int i=0;i<size;i++){
				serverVo=servers.get(i);
				if(this.serverComputers.containsKey(serverVo.getId())){
					serverVo.setServerStatus(ExamServerVO.ServerStatus.CONNECT.getVal());
				}else{
					serverVo.setClientOpenCount(0);
					serverVo.setServerStatus(ExamServerVO.ServerStatus.DISCONN.getVal());
				}
				this.examServers.put(serverVo.getId(), serverVo);
			}
		}
		
		// 加载服务器场次信息
		List<ServerRoundVO> serverRounds=this.examDataService.findServerRounds(1, 0, null);
		if(!serverRounds.isEmpty()){
			String key;
			ServerRoundVO serverRoundVo;
			ServerRoundHolder serverRoundHolder;
			int size=serverRounds.size();
			for(int i=0;i<size;i++){
				serverRoundVo=serverRounds.get(i);
				key=serverRoundVo.getRoundBeginTime().getTime()+"_"+serverRoundVo.getRoundEndTime().getTime();
				serverRoundHolder=this.serverRounds.get(key);
				if(serverRoundHolder==null){
					serverRoundHolder=new ServerRoundHolder(key, serverRoundVo.getRoundBeginTime(), serverRoundVo.getRoundEndTime());
					this.serverRounds.put(key, serverRoundHolder);
				}
				serverRoundHolder.addServerRound(serverRoundVo);
			}
		}
		// 加载考场场次信息
		List<RoomRoundVO> roomRounds=this.examDataService.findRoomServerRounds(1, 0, null);
		if(!roomRounds.isEmpty()){
			String key;
			RoomRoundVO roomRoundVO;
			RoomRoundHolder roomRoundHolder;
			int size=roomRounds.size();
			for(int i=0;i<size;i++){
				roomRoundVO=roomRounds.get(i);
				key=roomRoundVO.getRoundBeginTime().getTime()+"_"+roomRoundVO.getRoundEndTime().getTime();
				roomRoundHolder=this.roomRounds.get(key);
				if(roomRoundHolder==null){
					roomRoundHolder=new RoomRoundHolder(key, roomRoundVO.getRoundBeginTime(), roomRoundVO.getRoundEndTime());
					this.roomRounds.put(key, roomRoundHolder);
				}
				roomRoundHolder.addRoomRound(roomRoundVO);
			}
		}
		this.dataTime=System.currentTimeMillis();
	}

	private String getDefaultIpAddr() {
		List<NetInterfaceConfig> list=SigarUtils.getNetInterfaceConfigs();
		if(list==null || list.isEmpty()){
			return "127.0.0.1";
		}
		return list.get(0).getAddress();
	}
	/*
	 * 获取新的授权号
	 */
	private String nextAuth() {
		String dic="ABCDEFGHJKMNPQRSTUVWXYZ23456789";
		int len=dic.length();
		StringBuilder sb=new StringBuilder(6);
		for(int i=0;i<6;i++){
			sb.append(dic.charAt(RandomUtils.nextInt(len)));
		}
		return sb.toString();
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		/*beanutils 时间日期字符转换器*/
		ConvertUtils.register(new SqlDateConverter(null), java.sql.Date.class);
		ConvertUtils.register(new SqlTimestampConverter(null),java.sql.Timestamp.class);
		ConvertUtils.register(new UtilDateConverter(),java.util.Date.class);
		File f;
		try{
			//1、加载考试系统版本信息
			Version v=new Version();
			f=new File(SinotnRuntime.getHome(),"bin/dt0.dt");
			if(f.exists()){
				String xmlText=CommonSecretor.xorFileText(f);
				if(null!=xmlText){
					v.load(xmlText);
				}
			}
			this.version=v.getText();
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("监控引擎接口默认实现属性后加载方法调用(afterPropertiesSet)发生系统错误",e);
		}
		//2、读取考试机客户端版本号
		try{
			//1、加载考试系统版本信息
			Version v=new Version();
			f=new File(SinotnRuntime.getHome(),"client/gl.dt");
			if(f.exists()){
				String xmlText=CommonSecretor.xorFileText(f);
				if(null!=xmlText){
					v.load(xmlText);
				}
			}
			this.clientVersion=v.getText();
		}catch(Throwable e){
			SinotnLogger.DEBUG.error("监控引擎接口默认实现属性后加载方法调用(afterPropertiesSet)读取客户端版本号发生系统错误",e);
		}
		//异步加载引擎缓存数据
		this.taskServer.putAsyncTask(this);
	}

	@Override
	public List<ExamRoundBean> allExamRounds() {
		return this.examRounds;
	}

	@Override
	public List<ExamServerBean> getExamServers(Date beginTime, Date endTime, boolean force) {
		String key=beginTime.getTime()+"_"+endTime.getTime();
		if(force || this.dataTime>this.queryTime||!this.currentRound.equals(key)){
			this.currentRound=key;
			ServerRoundHolder holder=this.serverRounds.get(key);
			if(holder==null) return null;
			List<ServerRoundVO> rounds=holder.allServerRounds();
			if(rounds.isEmpty()) return null;
			int size=rounds.size();
			ServerRoundVO roundVo;
			ExamServerVO serverVo;
			ExamServerBean serverBean;
			ArrayList<ExamServerBean> list=new ArrayList<ExamServerBean>(size);
			for(int i=0;i<size;i++){
				roundVo=rounds.get(i);
				serverVo=this.examServers.get(roundVo.getExamServerId());
				serverBean=new ExamServerBean();
				list.add(serverBean);
				serverBean.setValues(serverVo, roundVo);
			}
			this.queryTime=System.currentTimeMillis();
			return list;
		}else{
			return Collections.emptyList();
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext appCtx) throws BeansException {
		Map<String, MessageReceiver> maps=appCtx.getBeansOfType(MessageReceiver.class);
		if(null!=maps&&maps.size()>0){
			MessageReceiver receiver;
			for(Iterator<MessageReceiver> it=maps.values().iterator();it.hasNext();){
				receiver=it.next();
				this.receivers.put(receiver.getType(), receiver);
			}
		}
	}

	@Override
	public void reloadData() {
		this.roomRounds.clear();
		this.serverRounds.clear();
		this.examServers.clear();
		this.loadData();
	}

	@Override
	public void lockServerReg() {
		this.serverRegLock.lock();
	}

	@Override
	public void unlockServerReg() {
		this.serverRegLock.unlock();
	}

	@Override
	public boolean hasRound(Date beginTime, Date endTime) {
		String key=beginTime.getTime()+"_"+endTime.getTime();
		return this.serverRounds.containsKey(key);
	}
	
	@Override
	public void backupServer(String serverId, File file) {
		ExamServerVO examServer=this.examServers.get(serverId);
		if(examServer!=null){
			File folder=new File(SinotnRuntime.getHome(),"backup");
			if(!folder.exists()) folder.mkdir();
			folder=new File(folder,"Server");
			if(!folder.exists()) folder.mkdir();
			SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
			File dest=new File(folder, examServer.getExamServerName()+"服务备份"+sdf.format(new Date())+".zip");
			FileUtil.copyFile(file, dest);
			examServer.setBackupTime(new Date());
			this.examDataService.saveExamServer(examServer);
			this.touchData();
		}
	}
	
	public ExamServerVO getExamServer(String serverId){
		if(serverId==null) return null;
		return this.examServers.get(serverId);
	}

	@Override
	public void updateExamInfo(String serverId, ExamRunInfo examRunInfo) {
		List<RoundExamInfo> rounds=examRunInfo.getRounds();
		RoundExamInfo roundExamInfo;
		ServerRoundVO serverRound;
		ServerRoundHolder serverRoundHolder;
		List<RoomExamInfo> roomExamInfos;
		RoomRoundHolder roomRoundHolder;
		RoomExamInfo roomExamInfo;
		RoomRoundVO roomRoundVo;
		int j,roomSize;
		String key;
		boolean update=false;
		boolean ok;
		int clientOpenCount=0;
		int invigilateCount;
		int loginCount;
		int submitCount;
		int size=rounds.size();
		for(int i=0;i<size;i++){
			roundExamInfo=rounds.get(i);
			key=roundExamInfo.getBeginTime().getTime()+"_"+roundExamInfo.getEndTime().getTime();
			roomRoundHolder=this.roomRounds.get(key);
			roomExamInfos=roundExamInfo.getRooms();
			roomSize=roomExamInfos.size();
			invigilateCount=0;
			loginCount=0;
			submitCount=0;
			for(j=0;j<roomSize;j++){
				roomExamInfo=roomExamInfos.get(j);
				clientOpenCount+=roomExamInfo.getClientOpenCount();
				roomRoundVo=roomRoundHolder.getRoomRound(serverId, roomExamInfo.getId());
				ok=false;
				if(roomExamInfo.getClientOpenCount()!=roomRoundVo.getClientOpenCount()){
					roomRoundVo.setClientOpenCount(roomExamInfo.getClientOpenCount());
					ok=true;
				}
				invigilateCount+=roomExamInfo.getInvigilator();
				if(roomExamInfo.getInvigilator()!=roomRoundVo.getInvigilatorStatus()){
					roomRoundVo.setInvigilatorStatus(roomExamInfo.getInvigilator());
					ok=true;
				}
				if(roomExamInfo.getLoginCount()>roomRoundVo.getLoginCount()){
					roomRoundVo.setLoginCount(roomExamInfo.getLoginCount());
					loginCount+=roomExamInfo.getLoginCount();
					ok=true;
				}
				if(roomExamInfo.getSubmitCount()>roomRoundVo.getSubmitCount()){
					roomRoundVo.setSubmitCount(roomExamInfo.getSubmitCount());
					submitCount+=roomExamInfo.getSubmitCount();
					ok=true;
				}
				if(ok){
					this.examDataService.saveRoomRound(roomRoundVo);
				}
			}
			serverRoundHolder=this.serverRounds.get(key);
			ok=false;
			serverRound=serverRoundHolder.getServerRound(serverId);
			if(serverRound.getPaperDown()!=roundExamInfo.getPaperDown()){
				serverRound.setPaperDown(roundExamInfo.getPaperDown());
				ok=true;
			}
			if(serverRound.getRoomSignCount()<invigilateCount){
				serverRound.setRoomSignCount(invigilateCount);
				ok=true;
			}
			if(serverRound.getLoginCount()<loginCount){
				serverRound.setLoginCount(loginCount);
				ok=true;
			}
			if(serverRound.getSubmitCount()<submitCount){
				serverRound.setSubmitCount(submitCount);
				ok=true;
			}
			if(ok){
				this.examDataService.saveServerRound(serverRound);
				update=true;
			}
		}
		ExamServerVO examServer=this.examServers.get(serverId);
		if(clientOpenCount!=examServer.getClientOpenCount()){
			examServer.setClientOpenCount(clientOpenCount);
			this.examDataService.saveExamServer(examServer);
			update=true;
		}
		if(update){
			this.touchData();
		}
	}

	@Override
	public void backupRoundData(String serverId, Date uploadTime, List<ExamRoundVO> examRounds, File bakFile) {
		ExamServerVO examServer=this.examServers.get(serverId);
		if(examServer!=null){
			Date beginTime=null;
			Date endTime=null;
			if(examRounds!=null){
				ExamRoundVO examRound;
				String key;
				ServerRoundVO serverRound;
				ServerRoundHolder serverRoundHolder;
				int size=examRounds.size();
				for(int i=0;i<size;i++){
					examRound=examRounds.get(i);
					if(beginTime==null || beginTime.after(examRound.getBeginTime())){
						beginTime=examRound.getBeginTime();
					}
					if(endTime==null || endTime.before(examRound.getEndTime())){
						endTime=examRound.getEndTime();
					}
					key=examRound.getBeginTime().getTime()+"_"+examRound.getEndTime().getTime();
					serverRoundHolder=this.serverRounds.get(key);
					if(serverRoundHolder!=null){
						serverRound=serverRoundHolder.getServerRound(serverId);
						if(serverRound!=null){
							serverRound.setUploadTime(uploadTime);
							serverRound.setBackupTime(new Date());
							this.examDataService.saveServerRound(serverRound);
						}
					}
				}
			}
			
			File folder=new File(SinotnRuntime.getHome(),"backup");
			if(!folder.exists()) folder.mkdir();
			folder=new File(folder,"Round");
			if(!folder.exists()) folder.mkdir();
			StringBuilder sb=new StringBuilder();
			sb.append(examServer.getExamServerName());
			sb.append("_");
			sb.append(this.getSuffix(beginTime, endTime));
			//sb.append(".bak");
			String filename=sb.toString();
			File dest=new File(folder, filename+".bak");
			if(dest.exists()){
				int index=1;
				do{
					dest=new File(folder, filename+"_"+(index++)+".bak");
				}while(dest.exists());
			}
			FileUtil.copyFile(bakFile, dest);
			this.touchData();
		}
	}

	private String getSuffix(Date beginTime, Date endTime) {
		if(beginTime==null){
			SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
			return sdf.format(new Date());
		}
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
		String text1=sdf.format(beginTime);
		String text2=sdf.format(endTime);
		if(text1.endsWith("00") && text2.endsWith("00")){
			text1=text1.substring(0, text1.length()-2);
			text2=text2.substring(0, text2.length()-2);
		}
		String dayPrefix=text1.substring(0, 8);
		StringBuilder sb=new StringBuilder();
		sb.append(text1);
		sb.append("T");
		if(text2.startsWith(dayPrefix)){
			sb.append(text2.substring(8));
		}else{
			sb.append(text2);
		}
		return sb.toString();
	}

	@Override
	public void removeServer(String serverId) {
		if(serverId==null) return;
		ExamServerVO examServer=this.examServers.remove(serverId);
		if(examServer!=null){
			this.examDataService.removeExamServer(serverId);
			this.reloadData();;
		}
	}
}
